/// https://dart.cn/guides/language/effective-dart/usage
/// https://dart.cn/guides/language/language-tour

/// 1、免直接使用 part  如果使用要在 part of 中使用字符串。
/// 2、不要 导入 package 中 src 目录下的库
/// 3、Don’t use /lib/ in import paths.
/// Don’t use ../ to escape the lib directory.
/// when you need to reach into a package’s lib directory   use a package: import.
/// import 'package:my_package/api.dart';
///
///
///
/// 4、不要初始化nulll  使用Item? bestItem; 而不是 Item? bestItem = null;
// void error([String? message]) {
//   stderr.write(message ?? '\n');
// }

/// 5、If you want null to be false: 不会将变量提升为 non-nullable type
// if (optionalThing?.isEnabled ?? false) {
//   print('Have enabled thing.');
// }

/// If you want null to be true:
// if (optionalThing?.isEnabled ?? true) {
//   print('Have enabled thing.');
// }

/// 6、如果要在if语句中将变量提升为non-nullable用 !=null更好
// int measureMessage(String? message) {
//   if (message != null && message.isNotEmpty) {
//     // message is promoted to String.提升为不可为null的确定string，可直接取length
//     return message.length;
//   }
//   return 0;
// }

// int measureMessage(String? message) {
//   if (message?.isNotEmpty ?? false) {
//     // message is not promoted to String. 未提升为string，使用!.取length
//     return message!.length;
//   }

//   return 0;
// }

/// 8、将可空字段复制到局部变量以启用类型提升以使用non-nullable.而不是频繁使用 !.
/// 9、两个字符串连接可以直接挨着写'aaaa''bbbb'适合不能放一行的长字符串 使用插值的形式来组合字符串和值 简单标识符不用{}
/// 10、集合使用字面量初始化
// var points = <Point>[];
// var addresses = <String, Address>{};
// var counts = <int>{};
// 而不是
// var addresses = Map<String, Address>();
// var counts = Set<int>();

/// 11、集合使用展开符而不是addAll和add添加，还可以在里面使用if和for
// var arguments = [
//   ...options,
//   command,
//   ...?modeFlags,
//   for (var path in filePaths)
//     if (path.endsWith('.dart'))
//       path.replaceAll('.dart', '.js')
// ];

/// 12、不要 使用 .length 来判断一个集合是否为空，而是.isEmpty 和.isNotEmpty
/// 13、避免 在 Iterable.forEach() 中使用字面量函数。 而是使用for-in
/// 14、不要 使用 List.from() 除非想修改结果的类型。而是使用.toList()
// Creates a List<int>:
// var iterable = [1, 2, 3];

// // Prints "List<dynamic>":
// print(List.from(iterable).runtimeType);
// // Prints "List<int>":
// print(iterable.toList().runtimeType);

/// 15、使用 whereType() 按类型过滤集合。
// var objects = [1, 'a', 2, 'b', 3];
// var ints = objects.where((e) => e is int); // 它返回了一个 Iterable<Object>
// var ints = objects.where((e) => e is int).cast<int>(); // cast 转化
// var ints = objects.whereType<int>(); // 直接使用whereType good

/// 16、不要 使用 cast()，如果有更合适的方法。
// var stuff = <dynamic>[1, 2];
// var ints = List<int>.from(stuff); // toList() ，那么请使用 List<T>.from() 替换，这里的 T 是你想要的返回值的类型
// var stuff = <dynamic>[1, 2];
// var reciprocals = stuff.map<double>((n) => 1 / n); //  map() ，给它一个显式的类型参数，这样它就能产生一个所需类型的可迭代对象

///用恰当的类型创建集合。 修改集合被首次创建时的代码，为集合提供有一个恰当的类型。
///在访问元素时进行 cast 操作。 如果要立即对集合进行迭代，在迭代内部 cast 每个元素。
// /// for (var n in objects) {
//     if ((n as int).isEven) print(n);
//   }
///逼不得已进行 cast，请使用 List.from()

///17、如果你有一个方法，这个方法调用了参数相同的另一个方法。那么，你不需要人为将这个方法包装到一个 lambda 表达式中。
// names.forEach(print); // good
// names.forEach((name) {
//   print(name);
// });

/// 18、 使用final 修饰本地不会重新赋值的变量 var 修饰可以重新指定的。也可以 final只修饰字段和顶级变量两条规则选其中一个
///
/// 19、避免 保存可计算的结果  使用更少的内存、减少出错的可能性。它尽可能少的保存了表示圆所需要的数据
/// good
// class Circle {
//   double radius;

//   Circle(this.radius);

//   double get area => pi * radius * radius;
//   double get circumference => pi * 2.0 * radius;
// }

/// bad 缓存   当计算结果比较费时的时候可能需要缓存，但是只应该在你只有你有这样的性能问题的时候再去处理
// class Circle {
//   double _radius;
//   double get radius => _radius;
//   set radius(double value) {
//     _radius = value;
//     _recalculate();
//   }

//   double _area = 0.0;
//   double get area => _area;

//   double _circumference = 0.0;
//   double get circumference => _circumference;

//   Circle(this._radius) {
//     _recalculate();
//   }

//   void _recalculate() {
//     _area = pi * _radius * _radius;
//     _circumference = pi * 2.0 * _radius;
//   }
// }

/// 20、不要 为字段创建不必要的 getter 和 setter 方法。
/// 字段和 getter/setter 是完全无法区分的
///
/// 21、推荐 使用 final 关键字来创建只读属性。
/// 22、考虑 对简单成员使用 => 。
/// 23、不要 使用 this.，在重定向命名函数和避免冲突的情况下除外。
/// JavaScript 需要使用 this. 来引用对象的成员变量，但是 Dart—和 C++, Java, 以及C#—没有这种限制。
/// 只有两种情况需要使用 this.，其中一种情况是要访问的局部变量和成员变量命名一样的时候：
// class Box {
//   Object? value;

//   void clear() {
//     update(null);
//   }

//   void update(Object? value) {
//     this.value = value;
//   }
// }
/// 另一种使用 this. 的情况是在重定向到一个命名函数的时候：
// class ShadeOfGray {
//   final int brightness;

//   ShadeOfGray(int val) : brightness = val;

//   ShadeOfGray.black() : this(0);

//   // But now it will!
//   ShadeOfGray.alsoBlack() : this.black();
// }

/// 24、要 尽可能的在定义变量的时候初始化变量值。
/// 当然，对于变量取值依赖构造函数参数的情况以及不同的构造函数取值也不一样的情况，则不适合本条规则。
///
/// 25、构造函数 要 尽可能的使用初始化形式。
/// 如果构造函数参数使用 this. 的方式来初始化字段，这时参数的类型被认为和字段类型相同。
/// 要 用 ; 来替代空的构造函数体 {}。
// class Point {
//   double x, y;
//   Point(this.x, this.y);
// }

/// 26、不要 使用 new。
/// 27、不要 冗余地使用 const 。
/// 在表达式一定是常量的上下文中，const 关键字是隐式的，不需要写，也不应该。这里包括：
// 一个字面量常量集合。
// 调用一个常量构造函数。
// 元数据注解。
// 一个常量声明的初始化方法。
// switch case 表达式—— case 和 : 中间的部分，不是 case 执行体。
// （默认值并不包含在这个列表中，因为在 Dart 将来的版本中可能会在支持非常量的默认值。）
// 基本上，任何地方用 new 替代 const 的写法都是错的，因为 Dart 2 中允许省略 const 。
// bad ，下面只有第一个const有必要
// const primaryColors = const [
//   const Color('red', const [255, 0, 0]),
//   const Color('green', const [0, 255, 0]),
//   const Color('blue', const [0, 0, 255]),
// ];

/// 28、避免 使用没有 on 语句的 catch。
/// 在大多数情况下，您应该有一个 on 子句，这样它能够捕获程序在运行时你所关注的限定类型的异常并进行恰当处理。
/// 不要 丢弃没有使用 on 语句捕获的异常。
/// 要 只在代表编程错误的情况下才抛出实现了 Error 的异常。
///
/// 要 使用 rethrow 来重新抛出捕获的异常。  如果你想重新抛出一个异常，推荐使用 rethrow 语句。 rethrow 保留了原来的异常堆栈信息。而 throw 会把异常堆栈信息重置为最后抛出的位置。
// try {
//   somethingRisky();
// } catch (e) {
//   if (!canHandle(e)) rethrow;
//   handle(e);
// }

/// 29、推荐 使用 async/await 而不是直接使用底层的特性。
///
/// 不要 在没有有用效果的情况下使用 async 。
// Future<int> fastestBranch(
//     Future<int> left, Future<int> right) {
//   return Future.any([left, right]);
// }

/// 30、考虑 使用高阶函数来转换事件流（stream）。
/// 31、避免 直接使用 Completer。
/// good
// Future<bool> fileContainsBear(String path) {
//   return File(path).readAsString().then((contents) {
//     return contents.contains('bear');
//   });
// }
/// good
// Future<bool> fileContainsBear(String path) async {
//   var contents = await File(path).readAsString();
//   return contents.contains('bear');
// }

/// 32、要 使用 Future<T> 对 FutureOr<T> 参数进行测试，以消除参数可能是 Object 类型的歧义。
// Future<T> logValue<T>(FutureOr<T> value) async {
//   if (value is Future<T>) {
//     var result = await value;
//     print(result);
//     return result;
//   } else {
//     print(value);
//     return value;
//   }
// }


/// 33、使用空安全，在使用一个变量前必须初始化值
// int lineCount;

// if (weLikeToCount) {
//   lineCount = countLines();
// } else {
//   lineCount = 0;
// }

// print(lineCount)

/// 34、late的两种使用场景
/// 1：确定在使用前会初始化值（编辑器检测不到的情况如：顶级变量和实例top-level variables and instance variables）
/// 2：用late标记后，只有在首次使用时才初始化值，如花费多、实例化需要使用this可以标记late。
/// late String temperature = _readThermometer(); // Lazily initialized.
///
///
/// 35、如果你不想更改一个变量，可以使用关键字 final 或者 const 修饰变量，这两个关键字可以替代 var 关键字或者加在一个具体的类型前。一个 final 变量只可以被赋值一次；一个 const 变量是一个编译时常量（const 变量同时也是 final 的）。顶层的 final 变量或者类的 final 变量在其第一次使用的时候被初始化。
/// 实例变量 可以是 final 的但不可以是 const，
/// 使用关键字 const 修饰变量表示该变量为 编译时常量。如果使用 const 修饰类中的变量，则必须加上 static 关键字
// const Object i = 3; // Where i is a const Object with an int value...
// const list = [i as int]; // Use a typecast.
// const map = {if (i is int) i: 'int'}; // Use is and collection if.
// const set = {if (list is List<int>) ...list}; // ...and a spread.

/// 36. Number
// // String -> int
// var one = int.parse('1');
// assert(one == 1);

// // String -> double
// var onePointOne = double.parse('1.1');
// assert(onePointOne == 1.1);

// // int -> String
// String oneAsString = 1.toString();
// assert(oneAsString == '1');

// // double -> String
// String piAsString = 3.14159.toStringAsFixed(2);
// assert(piAsString == '3.14');

/// 37、String
/// 使用三个单引号或者三个双引号也能创建多行字符串：
// var s1 = '''
// You can create
// multi-line strings like this one.
// ''';

/// 在字符串前加上 r 作为前缀创建 “raw” 字符串（即不会被做任何处理（比如转义）的字符串）：
/// var s = r'In a raw string, not even \n gets special treatment.';
/// 
/// 38、List
/// var list2 = [0, ...?list];
/// Dart 还同时引入了 集合中的 if 和 集合中的 for 操作
// var nav = [
//   'Home',
//   'Furniture',
//   'Plants',
//   if (promoActive) 'Outlet',
//   for (var i in listOfInts) '#$i'
// ];

/// 39、Sets
// var names = <String>{};
// // Set<String> names = {}; // This works, too.
// // var names = {}; // Creates a map, not a set.
// 可以在 Set 变量前添加 const 关键字创建一个 Set 编译时常量：
// final constantSet = const {
//   'fluorine',
//   'chlorine',
//   'bromine',
//   'iodine',
//   'astatine',
// };

/// 40、Maps
// var gifts = {
//   // Key:    Value
//   'first': 'partridge',
//   'second': 'turtledoves',
//   'fifth': 'golden rings'
// };
/// 你也可以使用 Map 的构造器创建 Map：
// var gifts = Map<String, String>();
// gifts['first'] = 'partridge';
/// 如果检索的 Key 不存在于 Map 中则会返回一个 null：
// var gifts = {'first': 'partridge'};
// assert(gifts['fifth'] == null);
/// 使用 .length 可以获取 Map 中键值对的数量：
///
/// 41、函数
/// 命名参数 命名参数默认为可选参数，除非他们被特别标记为 required。
// void enableFlags({bool? bold, required bool hidden}) {...}
// enableFlags(bold: true, hidden: false);
// 可选的位置参数
// String say(String from, String msg, [String? device])

/// 42、词法闭包
/// Dart 是词法有作用域语言，变量的作用域在写代码的时候就确定了，大括号内定义的变量只能在大括号内访问
/// 闭包 即一个函数对象，即使函数对象的调用在它原始作用域之外，依然能够访问在它词法作用域内的变量。
/// 函数可以封闭定义到它作用域内的变量。接下来的示例中，函数 makeAdder() 捕获了变量 addBy。无论函数在什么时候返回，它都可以使用捕获的 addBy 变量。
// Function makeAdder(int addBy) {
//   return (int i) => addBy + i;
// }

// void main() {
//   // Create a function that adds 2.
//   var add2 = makeAdder(2);

//   // Create a function that adds 4.
//   var add4 = makeAdder(4);

//   assert(add2(3) == 5);
//   assert(add4(3) == 7);
// }
/// 所有的函数都有返回值。没有显示返回语句的函数最后一行默认为执行 return null;。
/// 
/// 43、算术运算符
// assert(5 / 2 == 2.5); // Result is a double
// assert(5 ~/ 2 == 2); // Result is an int
// assert(5 % 2 == 1); // Remainder

/// 44、类型判断运算符
/// as 类型转换（也用作指定 类前缀)）
/// is	如果对象是指定类型则返回 true
/// is!	如果对象是指定类型则返回 false
/// 
/// 45、赋值运算符
/// a = value;
// // Assign value to b if b is null; otherwise, b stays the same
// b ??= value;

/// 46、按位和移位运算符 & | ^ ~ << >>
/// 
/// 47、条件表达式
/// 如果赋值是根据判定是否为 null 则考虑使用 ??。
/// String playerName(String? name) => name ?? 'Guest';
/// 48、级联运算符(.., ?..) 在同一个对象上连续调用多个对象的变量或方法